/**
* Calendar
*
* To get the selected value you have to define callback function 
* with only one argument - string representing a date. You can use
* strToDate() to get Date object from this string.
*
* Based on showCalendar() function by Denis Gritcyuk <denis@softcomplex.com>
*
* @author       Dmitry Prokudin <dmitry@actimind.com>
*
* @access       public
* @version      1.0
*/

function getPopupCalendar(strDateTime, today, yearFrom, yearTo, callbackFunc, layout, invertedDays)
{
    var yearFromStr = "";
    var yearToStr = "";
    if (yearFrom != null && yearFrom != "" && yearTo != null && yearTo != "")
    {
        yearFromStr = yearFrom+"-1-1";
        yearToStr = yearTo+"-12-31";
    }

    cal = new Calendar(strDateTime, today, yearFromStr, yearToStr, "", "", callbackFunc, true, "img/pixel.gif", layout, invertedDays);

    cal.setWeekDays(new Array("S", "M", "T", "W", "T", "F", "S"));

    return cal;
}

/**
* Calendar
*
* To get the selected value you have to define callback function 
* with only one argument - string representing a date. You can use
* strToDate() to get Date object from this string.
*
* Based on showCalendar() function by Denis Gritcyuk <denis@softcomplex.com>
*/
function printLayoutCalendar(strDateTime, today, startFrameDate, endFrameDate, callbackFunc, pixelPath,
                        layout, invertedDays)
{
    cal = new Calendar(strDateTime, today, "", "", startFrameDate, endFrameDate, callbackFunc, false, pixelPath, layout, invertedDays);
    cal.setStyleNames("calendarWorkingDay", "calendarWeekendDay", "calendarCurrentDay");
    document.writeln(cal.getCalendarHTML());
}

function createLayoutCalendar(strDateTime, today, startFrameDate, endFrameDate, callbackFunc, pixelPath,
                        layout, invertedDays)
{
    cal = new Calendar(strDateTime, today, "", "", startFrameDate, endFrameDate, callbackFunc, false, pixelPath, layout, invertedDays);
    cal.setStyleNames("calendarWorkingDay", "calendarWeekendDay", "calendarCurrentDay");
    return cal;
}

//////////////////////////////////////////////////////////////////////////
////////////////////////////   Calendar class   //////////////////////////
//////////////////////////////////////////////////////////////////////////

function Calendar(strDateTime, today, startDate, endDate, startFrameDate, endFrameDate, callbackFunc, inPopup,
                  pixelPath, layout, invertedDays)
{
    // define class methods
    this.getTodayDay            = Calendar_getTodayDay;
    this.getTodayYear           = Calendar_getTodayYear;
    this.getTodayMonth          = Calendar_getTodayMonth;
    this.getTodayMonthNumber    = Calendar_getTodayMonthNumber;
    this.openPopup              = Calendar_openPopup;
    this.setMonths              = Calendar_setMonths;
    this.setWeekDays            = Calendar_setWeekDays;
    this.getCalendarHTML        = Calendar_getCalendarHTML;
    this.getCurrentDay          = Calendar_getCurrentDay;
    this.getCurrentYear         = Calendar_getCurrentYear;
    this.getCurrentMonth        = Calendar_getCurrentMonth;
    this.getCurrentMonthNumber  = Calendar_getCurrentMonthNumber;
    this.getPrevMonth           = Calendar_getPrevMonth;
    this.getNextMonth           = Calendar_getNextMonth;
    this.getPrevMonthNumber     = Calendar_getPrevMonthNumber;
    this.getNextMonthNumber     = Calendar_getNextMonthNumber;
    this.getCustomDateURL       = Calendar_getCustomDateURL;
    this.getCustomDateJS        = Calendar_getCustomDateJS;
    this.hasUpDownNeighbourWithoutFrame = Calendar_hasUpDownNeighbourWithoutFrame;
    this.needDrawFrame          = Calendar_needDrawFrame;
    this.getCalendarRowHTML     = Calendar_getCalendarRowHTML;
    this.setStyleNames          = Calendar_setStyleNames;
    this.setHilightCurrentDay   = Calendar_setHilightCurrentDay;
    this.setLinkCurrentDay      = Calendar_setLinkCurrentDay;
    this.setHeader              = Calendar_setHeader;
    this.getMonthLabel          = Calendar_getMonthLabel;
    this.getYearLabel           = Calendar_getYearLabel;
    this.getStartDate           = Calendar_getStartDate;
    this.getEndDate             = Calendar_getEndDate;
    this.getCallback             = Calendar_getCallback;

    // init properties
    this.weekDays  = new Array();
    this.arrMonths = new Array();

    if(invertedDays != '' && invertedDays != null)
        this.invertedDays = invertedDays;
    else
        this.invertedDays = new Array();

    this.callbackFunc = callbackFunc;
    this.inPopup = inPopup;

    this.months = new Array("January", "February", "March",     "April",   "May",      "June",   
                            "July",    "August",   "September", "October", "November", "December");

    this.layout = layout;
    this.weekDays = new Array("Su", "Mo", "Tu", "We", "Th", "Fr", "Sa");
    if(layout == 'US') {
        this.weekStartDay = 0;
    }
    else {
        this.weekStartDay = 1;
    }


    this.today = strToDate(today);

    this.currentDate = strDateTime == null || strDateTime == "" ?  this.today : strToDate(strDateTime);
    this.startDate = null;
    this.endDate = null;
    if(startDate != null && startDate != "" && endDate != null && endDate != "")
    {
        this.startDate = strToDate(startDate);
        this.endDate = strToDate(endDate);
    }
    this.startFrameDate = null;
    this.endFrameDate = null;
    if(startFrameDate != null && startFrameDate != "" && endFrameDate != null && endFrameDate != "")
    {
        this.startFrameDate = strToDate(startFrameDate);
        this.endFrameDate = strToDate(endFrameDate);

    }


    this.prevMonth = new Date(this.currentDate);
    this.prevMonth.setMonth(this.currentDate.getMonth() - 1);

    this.nextMonth = new Date(this.currentDate);
    this.nextMonth.setMonth(this.currentDate.getMonth() + 1);

    this.firstDay = new Date(this.currentDate);
    this.firstDay.setDate(1);
    this.firstDay.setDate(1 - (7 + this.firstDay.getDay() - this.weekStartDay) % 7);

    this.lastDay = new Date(this.nextMonth);
    this.lastDay.setDate(0);

    this.pixelPath = pixelPath;
    this.hilightCurrentDay = true;
    this.linkCurrentDay = true;
    this.header = false;

    this.setStyleNames("calendarWorkingDay", "calendarWeekendDay", "calendarCurrentDay");
}

function Calendar_openPopup(title, params, calendarHtml)
{
    var calendarWnd = window.open("", title, params);
    calendarWnd.opener = self;
    calendarWnd.document.write(calendarHtml);
    calendarWnd.document.close();
}

function TableElement(text, style, linkStyle, link, hasFrame)
{
  this.text = text;
  this.style = style;
  this.linkStyle = linkStyle;
  this.link = link;
  this.hasFrame = hasFrame;
}

/**
* Styles you can use:
*
*  - calendarTable
*  - calendarWeekDay
*  - calendarCurrentDay
*  - calendarWorkingDay
*  - calendarWeekendDay
*  - calendarCurrentMonthDay
*  - calendarOtherMonthDay
*
*/
function Calendar_getCalendarHTML()
{
    var calendarTable = new Array();

    calendarTable[0] = new Array();
    for (var n = 0; n < 7; n++)
    {
        wd = (this.weekStartDay + n) % 7;
        style = wd == 0 || wd == 6 ? "calendarWeekendDayHeader" : "calendarWorkingDayHeader"
        calendarTable[0][n] = new TableElement(this.weekDays[wd], style, null, null, false);
    }

    var curWeek = 1;
    var curDay = new Date(this.firstDay);
    while (curDay.getMonth() == this.currentDate.getMonth() ||
           curDay.getMonth() == this.firstDay.getMonth()) 
    {
        calendarTable[curWeek] = new Array();
        for (var currentWeekDay = 0; currentWeekDay < 7; currentWeekDay++) 
        {
            var text;
            var style;
            var linkStyle;
            var link;
            var hasFrame;
            if (curDay.getFullYear() == this.today.getFullYear() &&
                curDay.getMonth()    == this.today.getMonth() &&
                curDay.getDate()     == this.today.getDate())
                currentDayNow = true;
            else
                currentDayNow = false;

            if (currentDayNow && this.hilightCurrentDay == true)
            {
                style = this.currentDayStyle;
            }
            else if (curDay.getDay() == 0 || curDay.getDay() == 6)
            {
                style = this.nonWorkDayStyle;
            }
            else
            {
                style = this.workDayStyle;
            }

            for(var i = 0; i < this.invertedDays.length; i++)
            {
                invDate = strToDate(this.invertedDays[i]);
                if(curDay.getFullYear() == invDate.getFullYear() &&
                   curDay.getMonth()    == invDate.getMonth() &&
                   curDay.getDate()     == invDate.getDate()) {
                        if(style == this.nonWorkDayStyle)
                            style = this.workDayStyle;
                        else
                            if(style == this.workDayStyle)
                                style = this.nonWorkDayStyle;
                   }
            }

            if (curDay.getMonth() == this.currentDate.getMonth())
            {
                linkStyle = "calendarCurrentMonthDay";
            }
            else 
            {
                linkStyle = "calendarOtherMonthDay";
            }
            text = curDay.getDate();
            if(currentDayNow != true || (currentDayNow == true && this.linkCurrentDay == true))
            {
                if(this.inPopup)
                    link="javascript: window.opener." + this.callbackFunc + "('" + dateToStr(curDay) + "');" +
                        "window.close();";
                else
                    link="javascript: " + this.callbackFunc + "('" + dateToStr(curDay) + "');";
            } else link = null;
            hasFrame = this.startFrameDate != null && this.endFrameDate != null
                && curDay.getTime() >= this.startFrameDate.getTime() && curDay.getTime() <= this.endFrameDate.getTime();
            calendarTable[curWeek][currentWeekDay] = new TableElement(text, style, linkStyle, link, hasFrame);
            curDay.setDate(curDay.getDate() + 1);
        }
        curWeek++;
    }

    var calendarHtml = "";

    calendarHtml += "<table width=\"100%\" border=0 cellspacing=0 cellpadding=0>\n";
    if(this.header)
       calendarHtml += "<tr><td></td><td class=\"currentMonthHeader\" colspan=\"27\" align=\"center\">" +
            this.getMonthLabel() + ", " + this.getYearLabel() + "</td>";


    for (var r = 0; r < calendarTable.length; r++) 
    {
        calendarHtml += this.getCalendarRowHTML(calendarTable, r,
            true, true, false, false, false);
        calendarHtml += this.getCalendarRowHTML(calendarTable, r,
            false, true, false, true, false);
        calendarHtml += this.getCalendarRowHTML(calendarTable, r,
            false, false, false, true, true);
        calendarHtml += this.getCalendarRowHTML(calendarTable, r,
            false, false, true, true, false);
        if(r == calendarTable.length - 1)
            calendarHtml += this.getCalendarRowHTML(calendarTable, r,
                false, false, true, false, false);
    }

    calendarHtml += "</table>\n";

    return calendarHtml;
}

function Calendar_getCalendarRowHTML(calendarTable, r, belongsToUp, upNeighbour, downNeighbour, styleFromTable, textFromTable)
{
    var html = "<tr>";
    for (var day = 0; day < 7; day++) 
    {
        var elem = calendarTable[r][day];
        // 1-st
        var style = "";
        if(this.needDrawFrame(calendarTable, r, day, belongsToUp, upNeighbour, downNeighbour, true, true, false))
        {
            style = " class=calendarFrame";
        }
        html += "<td nowrap=1 valign=middle" + style + "><img src='" + this.pixelPath + "'></td>";
        // 2-st
        style = "";
        if(styleFromTable)
            style = " class=" + elem.style;
        if(this.needDrawFrame(calendarTable, r, day, belongsToUp, upNeighbour, downNeighbour, false, true, false))
        {
            style = " class=calendarFrame";
        }
        html += "<td valign=middle nowrap=1 " + style + "><img src='" + this.pixelPath + "'></td>";
        // 3-st
        style = "";
        if(styleFromTable)
            style = " class=" + elem.style;
        if(this.needDrawFrame(calendarTable, r, day, belongsToUp, upNeighbour, downNeighbour, false, false, false))
        {
            style = " class=calendarFrame";
        }
        var content = "<img src='" + this.pixelPath + "'>";
        if(textFromTable)
        {
            style = " class=" + elem.style;
            if (elem.link)
            {
                content += "<span class=\"elemclass\"><a class='" + elem.linkStyle +
                    "' href=\"" + elem.link + "\">" + elem.text + "</a></span>";
            }
            else 
            {
                content += "<span class=\"elemclass\">" + elem.text+ "</span>";
            }
        }
        html += "<td valign=middle nowrap=1 " + style + ">" + content + "</td>";
        // 4-st
        style = "";
        if(styleFromTable)
            style = " class=" + elem.style;
        if(this.needDrawFrame(calendarTable, r, day, belongsToUp, upNeighbour, downNeighbour, false, false, true))
        {
            style = " class=calendarFrame";
        }
        html += "<td valign=middle nowrap=1 " + style + "><img src='" + this.pixelPath + "'></td>";
        if(day == 6)
        {
            // 5-st
            var style = "";
            if(this.needDrawFrame(calendarTable, r, day, belongsToUp, upNeighbour, downNeighbour, false, false, true))
            {
                style = " class=calendarFrame";
            }
            html += "<td valign=middle nowrap=1 " + style + "><img src='" + this.pixelPath + "'></td>";
        }
    }
    html += "</tr>\n";
    return html;
}

function Calendar_needDrawFrame(calendarTable, r, d, belongsToUp, upNeighbour, downNeighbour, belongsToLeft, leftNeighbour, rightNeighbour)
{
    var meWithFrame = false;
    var neighbourWithoutFrame = false;
    if(calendarTable[r][d].hasFrame)
        meWithFrame = true;
    if(belongsToUp && r > 0 && calendarTable[r-1][d].hasFrame)
        meWithFrame = true;
    if(belongsToLeft && d > 0 && calendarTable[r][d-1].hasFrame)
        meWithFrame = true;
    if(belongsToLeft && belongsToUp && d > 0 && r > 0 && calendarTable[r-1][d-1].hasFrame)
        meWithFrame = true;

    if(this.hasUpDownNeighbourWithoutFrame(calendarTable, r, d, upNeighbour, downNeighbour))
        neighbourWithoutFrame = true;

    if(leftNeighbour && d==0)
        neighbourWithoutFrame = true;
    if(leftNeighbour && d>0 && this.hasUpDownNeighbourWithoutFrame(calendarTable, r, d-1, upNeighbour, downNeighbour))
        neighbourWithoutFrame = true;

    if(rightNeighbour && d==6)
        neighbourWithoutFrame = true;
    if(rightNeighbour && d<6 && this.hasUpDownNeighbourWithoutFrame(calendarTable, r, d+1, upNeighbour, downNeighbour))
        neighbourWithoutFrame = true;

    return meWithFrame && neighbourWithoutFrame;
}

function Calendar_hasUpDownNeighbourWithoutFrame(calendarTable, r, d, upNeighbour, downNeighbour)
{
    if(!calendarTable[r][d].hasFrame)
        return true;
    if(upNeighbour && r==0)
        return true;
    if(upNeighbour && r>0 && !calendarTable[r-1][d].hasFrame)
        return true;
    if(downNeighbour && (r==calendarTable.length-1))
        return true;
    if(downNeighbour && (r<calendarTable.length-1) && !calendarTable[r+1][d].hasFrame)
        return true;
    return false;
}

function Calendar_setMonths(months) 
{
    this.months = months;
}

function Calendar_setHeader(header)
{
    this.header = header;
}

function Calendar_setLinkCurrentDay(linkCurDay)
{
   this.linkCurrentDay = linkCurDay;
}

function Calendar_setWeekDays(weekDays) 
{
    this.weekDays = weekDays;
}

function Calendar_getCallback()
{
    return this.callbackFunc;
}

function Calendar_getMonthLabel()
{
    return this.months[this.today.getMonth()];
}

function Calendar_getYearLabel()
{
    return this.today.getFullYear();
}



function Calendar_setStyleNames(workDayStyle, nonWorkDayStyle, currentDayStyle)
{
    this.workDayStyle = workDayStyle;
    this.nonWorkDayStyle = nonWorkDayStyle;
    this.currentDayStyle = currentDayStyle;
}


function Calendar_setHilightCurrentDay(doSelection)
{
    this.hilightCurrentDay = doSelection;
}

function Calendar_getTodayDay() 
{
    return this.today.getDate();
}

function Calendar_getTodayMonth() 
{
    return this.months[this.today.getMonth()];
}

function Calendar_getTodayMonthNumber() 
{
    return this.today.getMonth();
}

function Calendar_getTodayYear() 
{
    return this.today.getFullYear();
}

function Calendar_getCurrentDay() 
{
    return this.currentDate.getDate();
}

function Calendar_getCurrentMonth() 
{
    return this.months[this.currentDate.getMonth()];
}

function Calendar_getCurrentMonthNumber() 
{
    return this.currentDate.getMonth();
}

function Calendar_getCurrentYear() 
{
    return this.currentDate.getFullYear();
}

function Calendar_getNextMonth() 
{
    return this.months[this.prevMonth];
}

function Calendar_getPrevMonth() 
{
    return this.months[this.nextMonth];
}

function Calendar_getNextMonthNumber() 
{
    return this.months[this.prevMonth];
}

function Calendar_getPrevMonthNumber() 
{
    return this.months[this.nextMonth];
}

function Calendar_getCustomDateURL(year, month, day)
{
    return "javascript:window.opener.showCalendar('" + year + "-" + month + "-" + day + "', '" + dateToStr(this.today) + "', " + this.startDate.getFullYear() + ", " + this.endDate.getFullYear() + ", '" + this.callbackFunc + "');"
}

function Calendar_getCustomDateJS(year, month, day)
{
    return "window.opener.showCalendar('" + year + "-" + month + "-" + day + "', '" + dateToStr(this.today) + "', " + this.startDate.getFullYear() + ", " + this.endDate.getFullYear() + ", '" + this.callbackFunc + "');"
}

function Calendar_getEndDate()
{
   return this.endDate;
}

function Calendar_getStartDate()
{
   return this.startDate;
}

/**
* Styles you can use:
*
*  - calendarYearsSelector
*  - calendarMonthsSelector
*
* @param    pattern Pattern. YEAR lexeme will be replaced with year 
*                   selector and MONTH lexeme with months selector
*/

// YYYY-MM-DD
function strToDate(strDateTime) 
{
    var re = /^(\d+)\-(\d+)\-(\d+)$/;

    if (!re.exec(strDateTime))
        return alert("Invalid Datetime format: "+ strDateTime);

    return new Date(RegExp.$1, RegExp.$2 - 1, RegExp.$3);
}

// YYYY-MM-DD
function dateToStr(date) 
{
    return new String(date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate());
}

function getSelectedValue(selector)
{
    if (selector && selector.options)
        return selector.options[selector.selectedIndex].value;
}
